package schuchert.contest;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.net.URL;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.ibm.contest.instrumentation.ClassStreamInstrumentor;
import com.ibm.contest.instrumentation.InstrumentationAction;

public class DynamicInstrumentorTest {
    private static final String TEMP_DIR_PROP = DynamicInstrumentor.CON_TEST_INSTRUMENTATION_TEMP_DIR;
    private DynamicInstrumentor transformer;
    private String thisClassName;
    private String thisClassFileName;
    private File thisClassFile;
    private String originalPropertyValue;

    @Before
    public void setup() {
        resetTempDirProperty();
        transformer = new DynamicInstrumentor();
        transformer.deleteFilesAfterTransformation = false;
        thisClassName = getClass().getName().replaceAll("[.]", "/");
        thisClassFileName = thisClassName + ".class";
        thisClassFile = getClassFileForThisTestClass(thisClassFileName);
        assertTrue(thisClassFile.exists());
    }

    private void resetTempDirProperty() {
        originalPropertyValue = System.getProperty(TEMP_DIR_PROP);
        System.setProperty(TEMP_DIR_PROP, "TEST_TEMP_DIR");
    }

    @Test
    public void canTransformOneFile() throws Exception {

        File instrumentedFile = FileUtilities.generateDestinationFile(thisClassName, tempDirectory);

        try {
            byte[] originalFileBytes = getOriginalFileAsByteArray();
            ByteArrayInputStream inputFile = new ByteArrayInputStream(originalFileBytes);
            ClassStreamInstrumentor instrumentor = new ClassStreamInstrumentor(inputFile,
                    thisClassName, true);
            ByteArrayOutputStream outFile = new ByteArrayOutputStream(4 * originalFileBytes.length);
            instrumentor.instrumentInto(outFile, new InstrumentationAction(null, null));
            byte[] insrumentedFileBytes = outFile.toByteArray();
            assertTrue(originalFileBytes.length < insrumentedFileBytes.length);
        } finally {
            removeGeneratedFiles(instrumentedFile);
        }
    }

    @Test
    public void doesNotTransformIfFilterSetToExcludeThisClass() throws Exception {
        File instrumentedFile = FileUtilities.generateDestinationFile(thisClassName, tempDirectory);
        transformer.setFilter("-" + getClass().getCanonicalName());
        try {
            byte[] originalFileBytes = getOriginalFileAsByteArray();
            transformer.transform(null, thisClassName, null, null, originalFileBytes);
            assertTrue(intsrumentedFileDoesNotExist());
        } finally {
            removeGeneratedFiles(instrumentedFile);
        }
    }

    private byte[] getOriginalFileAsByteArray() {
        byte[] originalFileBytes = FileUtilities.readFileFully(thisClassFile);
        assertNotNull(originalFileBytes);
        return originalFileBytes;
    }

    private void removeGeneratedFiles(File instrumentedFile) {
        FileUtilities.removeFile(instrumentedFile);
        FileUtilities.removeFile(new File(instrumentedFile.getAbsoluteFile() + "_backup"));
        FileUtilities.removeDirectory(new File("com_ibm_contest"));
    }

    private boolean intsrumentedFileDoesNotExist() {
        return !generateInstrumentedFileObject().exists();
    }

    private File generateInstrumentedFileObject() {
        return FileUtilities.generateDestinationFile(thisClassName, tempDirectory);
    }

    private File getClassFileForThisTestClass(String thisClassFileName) {
        URL url = getClass().getClassLoader().getResource(thisClassFileName);
        File thisClassFile = new File(url.getFile());
        return thisClassFile;
    }
}
